package site.yunnong.atvris.portal.service.impl;

import cn.hutool.dfa.SensitiveUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import site.yunnong.atvris.common.api.ResultCodeEnum;
import site.yunnong.atvris.common.dto.UserRegisterFormParam;
import site.yunnong.atvris.common.enumeration.Constant;
import site.yunnong.atvris.common.enumeration.UserRoleEnum;
import site.yunnong.atvris.common.enumeration.UserStatusEnum;
import site.yunnong.atvris.common.exception.LocalAssert;
import site.yunnong.atvris.common.exception.YunnongBusinessException;
import site.yunnong.atvris.common.utils.IDUtils;
import site.yunnong.atvris.dao.domain.User;
import site.yunnong.atvris.portal.service.UserSsoService;
import site.yunnong.atvris.security.util.JwtTokenUtil;
import site.yunnong.atvris.service.EsUserService;
import site.yunnong.atvris.service.UserProfileService;
import site.yunnong.atvris.service.UserService;
import site.yunnong.atvris.service.domain.UmsUserDetails;
import site.yunnong.atvris.service.helper.OperatorHelper;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Random;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author JinghaoZhang
 * @date 2021年-04月-08日
 */
@Slf4j
@Service
public class UserSsoServiceImpl implements UserSsoService {

    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private JwtTokenUtil jwtTokenUtil;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private UserService userService;
    @Resource
    private UserProfileService userProfileService;
    @Resource
    private EsUserService esUserService;
    @Resource
    private ThreadPoolExecutor selfThreadPoolExecutor;

    @Override
    public String generateAuthCode(String username) {
        //  todo 验证码生成方式 现在先直接返回到界面，以后改
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            sb.append(random.nextInt(10));
        }
        //验证码绑定手机号并存储到redis
        redisTemplate.opsForValue().set(Constant.REDIS_KEY_PREFIX_AUTHCODE + Constant.REDIS_SPLIT + username, sb.toString());
        redisTemplate.expire(Constant.REDIS_KEY_PREFIX_AUTHCODE + Constant.REDIS_SPLIT + username, Constant.AUTHCODE_EXPIRE_SECONDS, TimeUnit.SECONDS);
        return sb.toString();
    }

    /**
     * 校验验证码
     *
     * @param username
     * @param authCode
     * @return
     */
    private boolean verifyAuthCode(String username, String authCode) {
        if (StringUtils.isEmpty(authCode)) {
            return false;
        }
        String realAuthCode = (String) redisTemplate.opsForValue().get(Constant.REDIS_KEY_PREFIX_AUTHCODE + Constant.REDIS_SPLIT + username);
        if (StringUtils.equals(realAuthCode, authCode)) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public String login(String username, String password, String authCode) {
        // 验证验证码是否正确
        if (!verifyAuthCode(username, authCode)) {
            // 全局处理器处理验证码错误异常
            throw new YunnongBusinessException(ResultCodeEnum.ERROR_PARAM_ILLEGAL, "验证码错误");
        }

        //密码需要客户端加密后传递
        // 之前request method not support应该是这里的问题
        UserDetails userDetails = loadUserDetailsByUsername(username);
        if (userDetails == null) {
            throw new YunnongBusinessException(ResultCodeEnum.ERROR_BUSINESS, "用户名不存在");
        }
        if (!passwordEncoder.matches(password, userDetails.getPassword())) {
            throw new YunnongBusinessException(ResultCodeEnum.ERROR_BUSINESS, "密码错误");
        }
        if (!userDetails.isEnabled()) {
            throw new YunnongBusinessException(ResultCodeEnum.ERROR_BUSINESS, "该账户已被封禁");
        }
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        return jwtTokenUtil.generateToken(userDetails);
    }

    @Override
    @Transactional
    public String register(UserRegisterFormParam param) {
        String username = param.getUsername();
        String nickname = param.getNickname();
        String password = param.getPassword();
        if (SensitiveUtil.containsSensitive(nickname)) {
            // 昵称包含敏感词
            throw new YunnongBusinessException(ResultCodeEnum.ERROR_PARAM_ILLEGAL, "输入昵称包含敏感词");
        }
        String authCode = param.getAuthCode();
        // 验证验证码是否正确
        if (!verifyAuthCode(username, authCode)) {
            // 全局处理器处理验证码错误异常
            throw new YunnongBusinessException(ResultCodeEnum.ERROR_PARAM_ILLEGAL, "验证码错误");
        }
        // 查询是否具有相同昵称或用户名的用户
        User userByNickname = userService.queryUserByNickname(nickname);
        LocalAssert.isNull(userByNickname, "该昵称已被使用");
        // 查询是否具有相同用户名的用户
        User userByUsername = userService.queryUserByUsername(username);
        LocalAssert.isNull(userByUsername, "该邮箱/手机号已被使用");
        // 添加该用户
        User user = initUser(nickname, password, username);
        int count = userService.insert(user);
        LocalAssert.notZero(count, "添加用户失败");
        // 异步添加用户统计信息与es数据
        selfThreadPoolExecutor.execute(() -> {
            userProfileService.insert(user.getUuid());
            esUserService.create(user.getUuid());
        });
        // 生成token
        UserDetails userDetails = loadUserDetailsByUsername(username);
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        return jwtTokenUtil.generateToken(userDetails);
    }

    @Override
    public UserDetails loadUserDetailsByUsername(String username) {
        User user = userService.queryUserByUsername(username);
        if (user != null) {
            return new UmsUserDetails(user);
        }
        throw new YunnongBusinessException(ResultCodeEnum.ERROR_BUSINESS, "用户名不存在");
    }

    @Override
    public String refreshToken(String tokenWithHeader) {
        String token = jwtTokenUtil.refreshHeadToken(tokenWithHeader);
        if (StringUtils.isEmpty(token)) {
            throw new YunnongBusinessException(ResultCodeEnum.ERROR_UNAUTHENTICATED);
        }
        return token;
    }

    @Override
    public boolean isLogin() {
        return OperatorHelper.isLogin();
    }

    private User initUser(String nickname, String password, String username) {
        return User.builder()
                .uuid(IDUtils.getId())
                .role(UserRoleEnum.MEMBER.getValue())
                .username(username).password(passwordEncoder.encode(password))
                .nickname(nickname)
                .personalSign("该用户没有个性签名")
                .status(UserStatusEnum.ACTIVE.getValue())
                .registerTime(LocalDateTime.now())
                // 其他项为默认
                .build();
    }
}
